/*
Copyright (c) 2020-2022, Huawei Technologies Co., Ltd
 All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

* Description: 3D图表可视化插件数据显示类
*/
#include "slt_space_display.h"

#include <QPixmap>
#include <QPainter>
#include <QIcon>

#include "common/utils.h"
#include "slt_space/slt_space_draw.h"

namespace chart_plugin {
using rviz::BoolProperty;
using rviz::ColorProperty;
using rviz::Config;
using rviz::EnumProperty;
using rviz::IntProperty;
using rviz::StatusProperty;
using rviz::StringProperty;
using std::make_unique;
using std::map;
using std::unique_ptr;

enum CurveType {
    OPTIMIZED_TRACK_CURVE,
    SPEED_LIMIT_CURVE,
};

const int32_t CURVE_LEGEND_MIN_HEIGHT_WIDTH = 22;
const int32_t COLOR_ALPHA_MAX = 0xFF;

bool SltSpaceDisplay::eventFilter(QObject *obj, QEvent *e)
{
    if (obj != &widget) {
        return QObject::eventFilter(obj, e);
    }

    if (e->type() != QEvent::Resize) {
        return QObject::eventFilter(obj, e);
    }

    ui.GetQMathGl()->setSize(ui.GetQMathGl()->parentWidget()->width(), ui.GetQMathGl()->parentWidget()->height());
    UpdateLegend();
    return true;
}

void SltSpaceDisplay::ClearData()
{
    optimizedCurveLegend->setChecked(true);
    speedLimitCurveLegend->setChecked(true);
    sltSpaceData.Clear();
}

void SltSpaceDisplay::InitTitleProperty()
{
    plotTitleProperty = new
        StringProperty("Chart Title", "SLT", "Chart title name property.", this, SLOT(UpdateMathglDraw()), this);
    plotTitleProperty->setReadOnly(true);

    xAxisTitleProperty = new
        StringProperty("X Axis Title", "S[m]", "X Axis title name property.", this, SLOT(UpdateMathglDraw()), this);
    xAxisTitleProperty->setReadOnly(true);

    yAxisTitleProperty = new
        StringProperty("Y Axis Title", "L[m]", "Y Axis title name property.", this, SLOT(UpdateMathglDraw()), this);
    yAxisTitleProperty->setReadOnly(true);

    zAxisTitleProperty = new
        StringProperty("Z Axis Title", "T[s]", "Z Axis title name property.", this, SLOT(UpdateMathglDraw()), this);
    zAxisTitleProperty->setReadOnly(true);
}

void SltSpaceDisplay::InitCurveProperty()
{
    const int32_t optimizedCurveColorR = 0xFF;
    const int32_t optimizedCurveColorG = 0x3B;
    const int32_t optimizedCurveColorB = 0x3B;
    const int32_t speedLimitCurveColorR = 0x00;
    const int32_t speedLimitCurveColorG = 0xFF;
    const int32_t speedLimitCurveColorB = 0xC1;

    curveProperty = new
        EnumProperty("Curve", "Optimized Track", "Curve type setting selection.", this, SLOT(UpdateCurveChoice()));
    curveProperty->addOption("Optimized Track", OPTIMIZED_TRACK_CURVE);
    curveProperty->addOption("Speed Limit", SPEED_LIMIT_CURVE);

    optimizedCurveTitleProperty = new StringProperty("Optimized Title", "Optimized Track",
        "Optimized curve title property setting.", curveProperty, SLOT(UpdateOptimizedCurveTitle()), this);

    optimizedCurveColorProperty = new
        ColorProperty("Optimized Color", QColor(optimizedCurveColorR, optimizedCurveColorG, optimizedCurveColorB),
        "Color of the Optimized Slt Track Curve.", curveProperty, SLOT(UpdateOptimizedCurveColor()), this);

    speedLimitCurveTitleProperty = new StringProperty("Speed Title", "Speed Limit",
        "Slt Speed Limit curve title property setting.", curveProperty, SLOT(UpdateSpeedLimitCurveTitle()), this);

    speedLimitCurveColorProperty = new
        ColorProperty("Speed Color", QColor(speedLimitCurveColorR, speedLimitCurveColorG, speedLimitCurveColorB),
        "Color of the Slt Speed Limit Curve.", curveProperty, SLOT(UpdateSpeedLimitCurveColor()), this);
}

void SltSpaceDisplay::InitProperty()
{
    InitTitleProperty();
    InitCurveProperty();
}

void SltSpaceDisplay::UpdateOptimizedCurveChoice()
{
    bool isOptimizedStTrackCurve = false;
    if (curveProperty->getOptionInt() == OPTIMIZED_TRACK_CURVE) {
        isOptimizedStTrackCurve = true;
    }

    optimizedCurveTitleProperty->setHidden(!isOptimizedStTrackCurve);
    optimizedCurveColorProperty->setHidden(!isOptimizedStTrackCurve);
}

void SltSpaceDisplay::UpdateStSpeedCurveChoice()
{
    bool isStSpeedLimitCurve = false;

    if (curveProperty->getOptionInt() == SPEED_LIMIT_CURVE) {
        isStSpeedLimitCurve = true;
    }

    speedLimitCurveTitleProperty->setHidden(!isStSpeedLimitCurve);
    speedLimitCurveColorProperty->setHidden(!isStSpeedLimitCurve);
}

void SltSpaceDisplay::UpdateCurveChoice()
{
    UpdateOptimizedCurveChoice();
    UpdateStSpeedCurveChoice();
}

void SltSpaceDisplay::CreateObjectsTypeProperty(int32_t type)
{
    QString typeName = QString("Type %1").arg(type);
    if (objectsTypeTotalVector.contains(type)) {
        return;
    }

    objectsTypeTotalVector.append(type);
    if (objectsTypeProperty == nullptr) {
        objectsTypeProperty = new EnumProperty("Objects Type", typeName,
            "Slt Objects Type setting selection.", this, SLOT(UpdateObjectsTypeChoice()));
    }
    objectsTypeProperty->addOption(typeName, type);
}

bool SltSpaceDisplay::SetObjectsTypeColor(int32_t type, const QColor &color)
{
    auto iter = objectsTypeColorHash.find(type);
    if (iter != objectsTypeColorHash.end()) {
        objectsTypeColorHash[type] = color;
        return true;
    }
    return false;
}

void SltSpaceDisplay::SetObjectsTypeColor(int32_t type, int32_t red, int32_t green, int32_t blue)
{
    auto iter = objectsTypeColorHash.find(type);

    QColor color;
    if (iter != objectsTypeColorHash.end()) {
        color = objectsTypeColorHash[type];
        color.setRed(red);
        color.setBlue(blue);
        color.setGreen(green);
        objectsTypeColorHash[type] = color;
        return;
    }
    color = QColor(red, green, blue, COLOR_ALPHA_MAX);
    objectsTypeColorHash[type] = color;
}

void SltSpaceDisplay::CreateObjectProperty(const QString &propertyKey, const QString &propertyValue)
{
    const int32_t valueKeyValueCharNum = 2;
    QStringList valueList = propertyValue.split(' ');
    if (propertyKey == "Value" && valueList.size() == valueKeyValueCharNum) {
        int32_t type = valueList[1].toInt();
        CreateObjectsTypeProperty(type);
        objectsTypeProperty->setString(propertyValue);
        return;
    }

    const int32_t keyCharNum = 3;
    QStringList keyList = propertyKey.split(' ');
    if (keyList[0] != "Type" || keyList.size() != keyCharNum) {
        ROS_ERROR("The configuration information is incorrect.");
        return;
    }

    int32_t type = keyList[1].toInt();
    CreateObjectsTypeProperty(type);
    const int32_t colorChannelNum = 3;
    if (keyList.contains("Color")) {
        QStringList colorChannelValue = propertyValue.split("; ");
        if (colorChannelValue.size() < colorChannelNum) {
            ROS_ERROR("color channel is incomplete .");
            return;
        }
        int32_t red = colorChannelValue[0].toInt();
        int32_t green = colorChannelValue[1].toInt();
        int32_t blue = colorChannelValue[2].toInt(); // 取colorChannelValue中第2个元素
        QColor color = QColor(red, green, blue);
        ColorProperty *objectsTypeColorPropertyPtr = new ColorProperty(propertyKey, color,
            "Objects type color setting.", objectsTypeProperty, SLOT(UpdateObjectsTypeColor()), this);

        SetObjectsTypeColor(type, red, green, blue);
        objectsTypeColorPropertyMap.insert(type, objectsTypeColorPropertyPtr);
        return;
    }

    if (keyList.contains("Title")) {
        StringProperty *objectsTypeTitlePropertyPtr = new StringProperty(propertyKey, propertyValue,
            "Objects type title setting.", objectsTypeProperty, SLOT(UpdateObjectsTypeTitle()), this);

        objectsTypeTitlePropertyMap.insert(type, objectsTypeTitlePropertyPtr);
    }
}

void SltSpaceDisplay::load(const rviz::Config &config)
{
    Display::load(config);
    rviz::Config configTmp = config.mapGetChild("Object Type");
    rviz::Config::MapIterator mapIter = configTmp.mapIterator();
    while (mapIter.isValid()) {
        QString configKey = mapIter.currentKey();
        rviz::Config childConfig = configTmp.mapGetChild(configKey);
        QString value = childConfig.getValue().toString();

        CreateObjectProperty(configKey, value);
        mapIter.advance();
    }

    UpdateObjectsTypeChoice();
}

void SltSpaceDisplay::CreateOptimizedLegend()
{
    if (optimizedCurveLegend == nullptr) {
        optimizedCurveLegend = new QPushButton(ui.GetScrollAreaWidgetContents());
        QObject::connect(optimizedCurveLegend, SIGNAL(toggled(bool)), this, SLOT(UpdateMathglDraw()));
    }

    QString title = optimizedCurveTitleProperty->getString();
    optimizedCurveLegend->setObjectName(title);
    optimizedCurveLegend->setMinimumHeight(CURVE_LEGEND_MIN_HEIGHT_WIDTH);
    optimizedCurveLegend->setIcon(GenerateLegendIcon(optimizedCurveColorProperty->getColor(), LegendType::CURVE));
    optimizedCurveLegend->setCheckable(true);
    optimizedCurveLegend->setChecked(true);
    optimizedCurveLegend->setText(title);
}

void SltSpaceDisplay::CreateSpeedLegend()
{
    if (speedLimitCurveLegend == nullptr) {
        speedLimitCurveLegend = new QPushButton(ui.GetScrollAreaWidgetContents());
        QObject::connect(speedLimitCurveLegend, SIGNAL(toggled(bool)), this, SLOT(UpdateMathglDraw()));
    }

    QString title = speedLimitCurveTitleProperty->getString();
    speedLimitCurveLegend->setObjectName(title);
    speedLimitCurveLegend->setMinimumHeight(CURVE_LEGEND_MIN_HEIGHT_WIDTH);
    speedLimitCurveLegend->setIcon(GenerateLegendIcon(speedLimitCurveColorProperty->getColor(), LegendType::CURVE));
    speedLimitCurveLegend->setCheckable(true);
    speedLimitCurveLegend->setChecked(true);
    speedLimitCurveLegend->setText(title);
}

const QIcon SltSpaceDisplay::GenerateLegendIcon(const QColor &color, LegendType type) const
{
    const int32_t positionX = 6;
    const int32_t positionY = 6;
    const int32_t width = 10;
    const int32_t height = 10;

    QPixmap pixmap(CURVE_LEGEND_MIN_HEIGHT_WIDTH, CURVE_LEGEND_MIN_HEIGHT_WIDTH);
    pixmap.fill(Qt::transparent);
    QPainter painter(&pixmap);
    painter.setPen(Qt::NoPen);

    QBrush brush;
    brush.setColor(color);
    brush.setStyle(Qt::SolidPattern);
    painter.setBrush(brush);

    if (type == LegendType::CURVE) {
        painter.drawEllipse(positionX, positionY, width, height);
    } else if (type == LegendType::OBJECT) {
        painter.drawRect(positionX, positionY, width, height);
    }

    return QIcon(pixmap);
}

void SltSpaceDisplay::CreateObjectsLegend(const QMap<QString, int32_t> &objectsIdType)
{
    if (!objectsIdLegendMap.empty()) {
        objectsIdLegendMap.clear();
    }
	
    for (auto iter = objectsIdType.begin(); iter != objectsIdType.end(); iter++) {
        QString objectId = iter.key();
        int32_t objectType = iter.value();

        const auto &idLegendIter = objectsIdLegendMap.find(objectId);
        if (idLegendIter != objectsIdLegendMap.end()) {
            continue;
        }

        if (!objectsTypeTitlePropertyMap.contains(objectType) || objectsTypeTitlePropertyMap[objectType] == nullptr) {
            continue;
        }
        auto buttonLegend = make_unique<QPushButton>();
        buttonLegend->setObjectName(QString("Type %1: %2").arg(objectType).arg(objectId));
        buttonLegend->setIcon(GenerateLegendIcon(GetObjectsTypeColor(objectType), LegendType::OBJECT));
        buttonLegend->setCheckable(true);
        buttonLegend->setChecked(sltSpaceData.GetObjectVisible(objectId));
        buttonLegend->setText(
            QString("%1: %2").arg(objectsTypeTitlePropertyMap[objectType]->getString()).arg(objectId));
        QObject::connect(buttonLegend.get(), SIGNAL(toggled(bool)), this, SLOT(UpdateMathglDraw()));
        objectsIdLegendMap.insert(std::make_pair(objectId, std::move(buttonLegend)));
    }
}

void SltSpaceDisplay::CalculateGridColumnAndRow(int32_t &column, int32_t &row)
{
    QFont font = widget.font();
    QFontMetrics metrics(font);

    int32_t width = metrics.width(optimizedCurveLegend->text()) + CURVE_LEGEND_MIN_HEIGHT_WIDTH;
    width += metrics.width(speedLimitCurveLegend->text()) + CURVE_LEGEND_MIN_HEIGHT_WIDTH;

    const int32_t curveLegendNum = 2;
    const int32_t horizontalSpacerNum = 2;
    const int32_t verticalSpacerNum = 2;
    int32_t widgetWidth = widget.width();
    const auto &objectsIdTypeMap = sltSpaceData.GetObjectsIdTypeMap();
    int32_t legendButtonNum = objectsIdTypeMap.size() + curveLegendNum;
    if (width >= widgetWidth) {
        column = horizontalSpacerNum + 1;
        row = horizontalSpacerNum + legendButtonNum;
        return;
    }

    // 固定的2个图例按钮宽度小于窗口宽度时，可以断定网格布局器至少4列（其中2个水平弹簧必占据2列）
    column = horizontalSpacerNum + curveLegendNum;
    row = verticalSpacerNum + 1;

    for (auto iter = objectsIdTypeMap.cbegin(); iter != objectsIdTypeMap.cend(); ++iter) {
        auto objectIdLegendIter = objectsIdLegendMap.find(iter.key());
        if ((objectIdLegendIter == objectsIdLegendMap.end()) || objectIdLegendIter->second == nullptr) {
            continue;
        }

        width += metrics.width(objectsIdLegendMap[iter.key()]->text()) + CURVE_LEGEND_MIN_HEIGHT_WIDTH;
        if (width >= widgetWidth) {
            break;
        }
        column += 1;
    }

    // 2个水平弹簧必占据2列，每一行包含(column - 2)个图例按钮
    if ((legendButtonNum % (column - horizontalSpacerNum)) != 0) {
        // 如果不能整除，则至少需要legendButtonNum / (column - 2) + 1 行放置图例按钮，再加上被2个垂直弹簧占据的2行
        row = legendButtonNum / (column - horizontalSpacerNum) + 1 + verticalSpacerNum;
    } else {
        row = legendButtonNum / (column - horizontalSpacerNum) + verticalSpacerNum;
    }
}

void SltSpaceDisplay::ResetGridLayoutItem()
{
    int32_t count = ui.GetGridLayout()->count();
    for (int32_t i = count - 1; i >= 0; i--) {
        QLayoutItem *item = ui.GetGridLayout()->itemAt(i);
        if (item == nullptr) {
            continue;
        }

        ui.GetGridLayout()->removeItem(item);
        QWidget *widgetItem = item->widget();
        if (widgetItem == nullptr) {
            continue;
        }

        for (auto objectIdLegendIter = objectsIdLegendMap.cbegin(); objectIdLegendIter != objectsIdLegendMap.cend();
            ++objectIdLegendIter) {
            if (widgetItem != (*objectIdLegendIter).second.get()) {
                continue;
            }

            QString objectId = (*objectIdLegendIter).first;
            if (!sltSpaceData.GetObjectsIdTypeMap().contains(objectId)) {
                widgetItem->hide();
            }
            break;
        }
    }
}

void SltSpaceDisplay::UpdateGridLayoutItem()
{
    int32_t column = 0;
    int32_t row = 0;
    CalculateGridColumnAndRow(column, row);
    ui.GetGridLayout()->addItem(verticalSpacerTop, 0, 1, 1, 1);
    ui.GetGridLayout()->addItem(verticalSpacerBottom, row - 1, 1, 1, 1);
    ui.GetGridLayout()->addItem(horizontalSpacerLeft, 1, 0, 1, 1);
    ui.GetGridLayout()->addItem(horizontalSpacerRight, 1, column - 1, 1, 1);

    const auto &objectsTypeIdMap = sltSpaceData.GetObjectsTypeIdMap();

    QList<int32_t> listType = objectsTypeIdMap.keys();

    QStringList listId;
    for (auto type : listType) {
        QStringList listObjectId = objectsTypeIdMap.value(type);
        qSort(listObjectId.begin(), listObjectId.end());
        listId << listObjectId;
    }

    int32_t k = 0;
    // 从第1行第1列位置开始放置图例按钮，遍历网格图例按钮放置区域(row - 2)行乘(column - 2)列个放置空间
    for (int32_t i = 1; i <= row - 2; i++) {
        // 共有（column - 2）列是安置图例按钮的
        for (int32_t j = 1; j <= column - 2; j++) {
            if (i == 1 && j == 1) {
                ui.GetGridLayout()->addWidget(optimizedCurveLegend, 1, 1, 1, 1);
                optimizedCurveLegend->show();
                // 如果column为3，则speedLimitCurveLegend按钮安置在第2行第1列位置，如果不是则安置在第1行第2列位置
            } else if ((i == 1 && j == 2) || (i == 2 && column == 3)) {
                ui.GetGridLayout()->addWidget(speedLimitCurveLegend, i, j, 1, 1);
                speedLimitCurveLegend->show();
                // 只有当整个障碍物图例按钮都还没有遍历安置完毕，网格布局器才安置图例按钮
            } else if (k < listId.count()) {
                QString objectId = listId.at(k);
				
                auto objectIdLegendIter = objectsIdLegendMap.find(objectId);
                if ((objectIdLegendIter == objectsIdLegendMap.end()) || (objectIdLegendIter->second == nullptr)) {
                    k++;
                    continue;
                }
                ui.GetGridLayout()->addWidget(objectsIdLegendMap[objectId].get(), i, j, 1, 1);
                objectsIdLegendMap[objectId]->show();
                k++;
            }
        }
    }
}

void SltSpaceDisplay::UpdateLegend()
{
    ResetGridLayoutItem();
    UpdateGridLayoutItem();
}

void SltSpaceDisplay::InitLegendSpacerItem()
{
    const int32_t verticalWidth = 20;
    const int32_t verticalHeightTop = 15;
    const int32_t verticalHeightBottom = 30;
    const int32_t horizontalWidth = 40;
    const int32_t horizontalHeight = 20;

    verticalSpacerTop =
        new QSpacerItem(verticalWidth, verticalHeightTop, QSizePolicy::Minimum, QSizePolicy::Expanding);

    ui.GetGridLayout()->addItem(verticalSpacerTop, 0, 1, 1, 1);
    verticalSpacerBottom = new
        QSpacerItem(verticalWidth, verticalHeightBottom, QSizePolicy::Minimum, QSizePolicy::Expanding);

    // 底部垂直弹簧放置第2行第1列，占据1行1列
    ui.GetGridLayout()->addItem(verticalSpacerBottom, 2, 1, 1, 1);
    horizontalSpacerLeft =
        new QSpacerItem(horizontalWidth, horizontalHeight, QSizePolicy::Expanding, QSizePolicy::Minimum);

    ui.GetGridLayout()->addItem(horizontalSpacerLeft, 1, 0, 1, 1);
    horizontalSpacerRight =
        new QSpacerItem(horizontalWidth, horizontalHeight, QSizePolicy::Expanding, QSizePolicy::Minimum);

    // 默认安装位置为第1行第3列，占据1行1列
    ui.GetGridLayout()->addItem(horizontalSpacerRight, 1, 3, 1, 1);
}

void SltSpaceDisplay::UpdateDrawQMathGl() const
{
    mgl_reset_frames(ui.GetQMathGl()->gr);
    if (mgl_is_frames(ui.GetQMathGl()->gr)) {
        mgl_new_frame(ui.GetQMathGl()->gr);
    }
    if (ui.GetQMathGl()->getClass()) {
        mglGraph graph(ui.GetQMathGl()->gr);
        ui.GetQMathGl()->getClass()->Draw(&graph);
    }
    if (mgl_is_frames(ui.GetQMathGl()->gr)) {
        mgl_end_frame(ui.GetQMathGl()->gr);
    }
    ui.GetQMathGl()->refresh();
}

bool SltSpaceDisplay::IsObjectsSizeAndIdSame(const QMap<QString, int32_t> &objectsIdType) const
{
    if (objectsIdType.size() == objectsIdLegendMap.size()) {
        for (auto iter = objectsIdLegendMap.begin(); iter != objectsIdLegendMap.end(); iter++) {
            if (!objectsIdType.contains(iter->first)) {
                return false;
            }
        }
        return true;
    }
    return false;
}

void SltSpaceDisplay::onInitialize()
{
    InitProperty();
    ChartDisplayClass::onInitialize();
    ui.SetupUi(&widget);
    InitLegendSpacerItem();
    CreateOptimizedLegend();
    CreateSpeedLegend();
    UpdateLegend();
    widget.installEventFilter(this);

    const double mglGraphColorR = 20 / 255.f;
    const double mglGraphColorG = 21 / 255.f;
    const double mglGraphColorB = 24 / 255.f;
    const int32_t mglGraphKind = 0;
    const int32_t mglGraphTransparencyType = 0;
    mglGraph graph(mglGraphKind, ui.GetQMathGl()->parentWidget()->width(), ui.GetQMathGl()->parentWidget()->height());
    graph.SetTranspType(mglGraphTransparencyType);
    graph.Clf(mglGraphColorR, mglGraphColorG, mglGraphColorB);
    ui.GetQMathGl()->setGraph(&graph);

    SltSpaceDraw *spaceDraw = new SltSpaceDraw(this, &sltSpaceData);

    ui.GetQMathGl()->setDraw(spaceDraw);
    ui.GetQMathGl()->setDotsPreview(false);
    ui.GetQMathGl()->setRotate(true);
    ui.GetQMathGl()->setZoom(false);
    UpdateCurveChoice();
    setAssociatedWidget(&widget);
    QObject::connect(ui.GetRestoreButton(), SIGNAL(pressed()), this, SLOT(UpdateMathglZoomAndRotation()));

    const int32_t tms = 1000;
    timer.setInterval(tms);
    connect(&timer, &QTimer::timeout, this, [=]() {
        UpdateDrawQMathGl();
    });
    timer.start();
}

void SltSpaceDisplay::UpdateMathglDraw()
{
    QPushButton *button = qobject_cast<QPushButton*>(sender());
    if (button != nullptr) {
        for (auto iter = objectsIdLegendMap.begin(); iter != objectsIdLegendMap.end(); iter++) {
            if (iter->second.get() == button) {
                sltSpaceData.SetObjectVisible(iter->first, button->isChecked());
            }
        }
    }

    UpdateDrawQMathGl();
}

void SltSpaceDisplay::UpdateMathglZoomAndRotation()
{
    ui.GetQMathGl()->restore();
    ui.GetQMathGl()->setRotate(true);
}

void SltSpaceDisplay::UpdateOptimizedCurveTitle()
{
    optimizedCurveLegend->setText(optimizedCurveTitleProperty->getString());
    UpdateLegend();
}

void SltSpaceDisplay::UpdateOptimizedCurveColor()
{
    optimizedCurveLegend->setIcon(GenerateLegendIcon(optimizedCurveColorProperty->getColor(), LegendType::CURVE));
    UpdateDrawQMathGl();
}

void SltSpaceDisplay::UpdateSpeedLimitCurveColor()
{
    speedLimitCurveLegend->setIcon(GenerateLegendIcon(speedLimitCurveColorProperty->getColor(), LegendType::CURVE));
    UpdateDrawQMathGl();
}

void SltSpaceDisplay::UpdateSpeedLimitCurveTitle()
{
    speedLimitCurveLegend->setText(speedLimitCurveTitleProperty->getString());
    UpdateLegend();
}

void SltSpaceDisplay::UpdateObjectsTypeChoice()
{
    if (objectsTypeProperty == nullptr) {
        return;
    }

    int32_t type = objectsTypeProperty->getOptionInt();
    for (auto iter : objectsTypeTotalVector) {
        if (!objectsTypeTitlePropertyMap.contains(iter) || (objectsTypeTitlePropertyMap[iter] == nullptr)) {
            continue;
        }

        if (!objectsTypeColorPropertyMap.contains(iter) || (objectsTypeColorPropertyMap[iter] == nullptr)) {
            continue;
        }

        objectsTypeTitlePropertyMap[iter]->setHidden(!(iter == type));
        objectsTypeColorPropertyMap[iter]->setHidden(!(iter == type));
    }
}

QColor SltSpaceDisplay::GetObjectsTypeColor(int32_t type)
{
    auto iter = objectsTypeColorHash.find(type);
    if (iter != objectsTypeColorHash.end()) {
        return iter.value();
    }

    const int32_t colorMin = 0x00;
    const int32_t colorMax = 0xFF;
    QColor color = QColor(GenerateRandom(colorMin, colorMax), GenerateRandom(colorMin, colorMax),
        GenerateRandom(colorMin, colorMax), COLOR_ALPHA_MAX);
    objectsTypeColorHash[type] = color;
    return color;
}

void SltSpaceDisplay::UpdateObjectsTypeColor()
{
    int32_t type = objectsTypeProperty->getOptionInt();
    if (!objectsTypeColorPropertyMap.contains(type) || (objectsTypeColorPropertyMap[type] == nullptr)) {
        return;
    }

    QColor objectColor = (objectsTypeColorPropertyMap[type])->getColor();

    const auto objectsIdTypeMap = sltSpaceData.GetObjectsIdTypeMap();
    for (auto iter = objectsIdTypeMap.begin(); iter != objectsIdTypeMap.end(); iter++) {
        auto objectIdLegendIter = objectsIdLegendMap.find(iter.key());
        if ((objectIdLegendIter != objectsIdLegendMap.end()) && (iter.value() == type)) {
            ((*objectIdLegendIter).second)->setIcon(GenerateLegendIcon(objectColor, LegendType::OBJECT));
        }
    }

    SetObjectsTypeColor(type, objectColor);
    UpdateDrawQMathGl();
}

void SltSpaceDisplay::UpdateObjectsTypeTitle()
{
    int32_t type = objectsTypeProperty->getOptionInt();
    const auto objectsIdTypeMap = sltSpaceData.GetObjectsIdTypeMap();
    for (auto iter = objectsIdTypeMap.begin(); iter != objectsIdTypeMap.end(); iter++) {
        auto objectIdLegendIter = objectsIdLegendMap.find(iter.key());
        if ((objectIdLegendIter != objectsIdLegendMap.end()) && (iter.value() == type) &&
            (objectsTypeTitlePropertyMap.contains(type) && objectsTypeTitlePropertyMap[type] != nullptr)) {
            ((*objectIdLegendIter).second)
                ->setText(QString("%1: %2").arg(objectsTypeTitlePropertyMap[type]->getString()).arg(iter.key()));
        }
    }

    UpdateLegend();
}

void SltSpaceDisplay::SetObjectProperty(int32_t objectType)
{
    if (objectsTypeTotalVector.contains(objectType)) {
        return;
    }

    objectsTypeTotalVector.append(objectType);
    if (objectsTypeProperty == nullptr) {
        objectsTypeProperty = new EnumProperty("Object Type", QString("Type %1").arg(objectType),
            "Slt Objects Type setting selection.", this, SLOT(UpdateObjectsTypeChoice()));
    }

    QString typeName = QString("Type %1").arg(objectType);
    objectsTypeProperty->addOption(typeName, objectType);
    StringProperty *objectsTypeTitlePropertyPtr =
        new StringProperty(QString("Type %1 Title").arg(objectType), QString("Type %1").arg(objectType),
        "Objects type title setting.", objectsTypeProperty, SLOT(UpdateObjectsTypeTitle()), this);

    QColor color = GetObjectsTypeColor(objectType);
    ColorProperty *objectsTypeColorPropertyPtr =
        new  ColorProperty(QString("Type %1 Color").arg(objectType), color, "Objects type color setting.",
        objectsTypeProperty, SLOT(UpdateObjectsTypeColor()), this);

    objectsTypeTitlePropertyMap.insert(objectType, objectsTypeTitlePropertyPtr);
    objectsTypeColorPropertyMap.insert(objectType, objectsTypeColorPropertyPtr);
    UpdateObjectsTypeChoice();
}

void SltSpaceDisplay::ProcessMessage(const custom::SltSpace::ConstPtr &message)
{
    if (message == nullptr) {
        setStatus(StatusProperty::Error, "Topic", "Topic Message is invalid!");
        return;
    }

    if (!sltSpaceData.Add(message)) {
        return;
    }

    isTopicMessageComing = true;
    const double mglGraphColorR = 20 / 255.f;
    const double mglGraphColorG = 21 / 255.f;
    const double mglGraphColorB = 24 / 255.f;
    const int32_t mglGraphKind = 0;
    const int32_t mglGraphTransparencyType = 0;
    mglGraph graph(mglGraphKind, ui.GetQMathGl()->parentWidget()->width(), ui.GetQMathGl()->parentWidget()->height());
    graph.SetTranspType(mglGraphTransparencyType);
    graph.Clf(mglGraphColorR, mglGraphColorG, mglGraphColorB);

    ui.GetQMathGl()->setGraph(&graph);
    ui.GetQMathGl()->setDotsPreview(false);
    ui.GetQMathGl()->setRotate(true);
    ui.GetQMathGl()->setZoom(false);
}

void SltSpaceDisplay::updateTopic()
{
    ClearData();
    UpdateDrawQMathGl();
    ChartDisplayClass::updateTopic();
}

StringProperty *SltSpaceDisplay::GetPlotTitleProperty() const
{
    return plotTitleProperty;
}

StringProperty *SltSpaceDisplay::GetXAxisTitleProperty() const
{
    return xAxisTitleProperty;
}

StringProperty *SltSpaceDisplay::GetYAxisTitleProperty() const
{
    return yAxisTitleProperty;
}

StringProperty *SltSpaceDisplay::GetZAxisTitleProperty() const
{
    return zAxisTitleProperty;
}

ColorProperty *SltSpaceDisplay::GetOptimizedCurveColorProperty() const
{
    return optimizedCurveColorProperty;
}

ColorProperty *SltSpaceDisplay::GetSpeedLimitCurveColorProperty() const
{
    return speedLimitCurveColorProperty;
}

const QMap<int32_t, ColorProperty *> &SltSpaceDisplay::GetObjectsTypeColorPropertyMap() const
{
    return objectsTypeColorPropertyMap;
}

QPushButton *SltSpaceDisplay::GetOptimizedCurveLegend() const
{
    return optimizedCurveLegend;
}

QPushButton *SltSpaceDisplay::GetSpeedLimitCurveLegend() const
{
    return speedLimitCurveLegend;
}

const map<QString, unique_ptr<QPushButton>> &SltSpaceDisplay::GetObjectsIdLegendMap() const
{
    return objectsIdLegendMap;
}

bool SltSpaceDisplay::GetIsTopicMessageComing() const
{
    return isTopicMessageComing;
}

void SltSpaceDisplay::SetIsTopicMessageComing(bool is)
{
    isTopicMessageComing = is;
}
}

// 声明此类是一个rviz的插件
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(chart_plugin::SltSpaceDisplay, rviz::Display)
